#include "can_manage.h"

static CanManage *g_pCanManage = nullptr;


/*
 * Function:    CanManage
 * Description: The default constructor
 * Paramer:     void
 * Return:
 */
CanManage::CanManage(QObject *parent) : QThread(parent), m_semaphore(0)
{
    qDebug() << Q_FUNC_INFO;
}

/*
 * Function:    ~CanManage
 * Description: The default destructor
 * Paramer:     void
 * Return:
 */
CanManage::~CanManage()
{
    qDebug() << Q_FUNC_INFO;
}

/*
 * Function:    getInstance
 * Description: Get an instance of "CanManage" class, if exist, return it
 * Paramer:     QObject *pParent - the pointer of QObject class
 * Return:      the pointer of "CanManage" class
 */
CanManage* CanManage::getInstance(QObject *pParent)
{
    qDebug() << Q_FUNC_INFO;

    if(!g_pCanManage) {
        // Create a new object if it doesn't exist
        g_pCanManage = new CanManage(pParent);
        if(!g_pCanManage) return nullptr;

        // After constructing object, initialize the object
        g_pCanManage->_initialze();
    }

    return g_pCanManage;
}

/*
 * Function:    _initialze
 * Description: After constructing object, initialize the object
 * Paramer:     void
 * Return:      void
 */
void CanManage::_initialze()
{
    qDebug() << Q_FUNC_INFO;
}

/*
 * Function:    run
 * Description: Rewrite the run() function of QThread
 * Paramer:     void
 * Return:      void
 */
void CanManage::run()
{
    qDebug() << Q_FUNC_INFO;

    while(!m_bExitThread) {
        m_semaphore.acquire();

#ifdef QT_PROJECT_T4
    // Concatenated data packet from CAN
    for(int i=0; i<DEF_IAP_MODULE_SIZE; i++) {
        _concatenatedDataFromIAP(i);
    }

    qApp->processEvents(QEventLoop::AllEvents, 20);
#elif QT_PROJECT_T5
    // Concatenated data packet from CAN
    for(int i=0; i<DEF_IAP_MODULE_SIZE; i++) {
        _concatenatedDataFromIAP(i);
    }

    qApp->processEvents(QEventLoop::AllEvents, 20);
#else
    // Concatenated data packet from CAN
    for(int i=0; i<DEF_IAP_MODULE_SIZE; i++) {
        _concatenatedDataFromIAP(i);
    }

    for(int i=0; i<DEF_PCR_MODULE_SIZE; i++) {
        _concatenatedDataFromPCR(i);
    }

    qApp->processEvents(QEventLoop::AllEvents, 50);
#endif
    }

    qDebug() << Q_FUNC_INFO << "0";
}

/*
 * Function:    slotFramesReceived
 * Description: One or more frames have been received by the CAN bus
 * Paramer:     unsigned int unCanId - CAN ID
 *              QByteArray baPayload - frame content
 * Return:      void
 */
void CanManage::slotFramesReceived(unsigned int unCanId, QByteArray baPayload)
{
    qDebug() << Q_FUNC_INFO << unCanId << baPayload.size();

    QMutexLocker mtxLocker(&m_mtxRecv);

    // Cache frame data
    if(0 < baPayload.size()) {
        int nIapModuleIdx = -1;
        int nPcrModuleIdx = -1;

        // Filter frame data
        switch (unCanId) {
        case DEF_MOD_ID_IAP1 + 1: { nIapModuleIdx = 0; break; }
        case DEF_MOD_ID_IAP2 + 1: { nIapModuleIdx = 1; break; }
        case DEF_MOD_ID_IAP3 + 1: { nIapModuleIdx = 2; break; }
        case DEF_MOD_ID_IAP4 + 1: { nIapModuleIdx = 3; break; }
        case DEF_MOD_ID_IAP5 + 1: { nIapModuleIdx = 4; break; }
        case DEF_MOD_ID_IAP6 + 1: { nIapModuleIdx = 5; break; }
        case DEF_MOD_ID_IAP7 + 1: { nIapModuleIdx = 6; break; }
        case DEF_MOD_ID_IAP8 + 1: { nIapModuleIdx = 7; break; }

        case DEF_MOD_ID_PCR1 + 1: { nPcrModuleIdx = 0; break; }
        case DEF_MOD_ID_PCR2 + 1: { nPcrModuleIdx = 1; break; }
        case DEF_MOD_ID_PCR3 + 1: { nPcrModuleIdx = 2; break; }
        case DEF_MOD_ID_PCR4 + 1: { nPcrModuleIdx = 3; break; }
        case DEF_MOD_ID_PCR5 + 1: { nPcrModuleIdx = 4; break; }
        case DEF_MOD_ID_PCR6 + 1: { nPcrModuleIdx = 5; break; }
        case DEF_MOD_ID_PCR7 + 1: { nPcrModuleIdx = 6; break; }
        case DEF_MOD_ID_PCR8 + 1: { nPcrModuleIdx = 7; break; }
        default: break;
        }

        if(0 <= nIapModuleIdx && DEF_IAP_MODULE_SIZE > nIapModuleIdx) {
            qDebug() << Q_FUNC_INFO << "--------- Recv Data A" << nIapModuleIdx << baPayload.size() << baPayload.toHex(' ').toUpper();

            // Write an frame data
            m_bufRecvIAP[nIapModuleIdx].write(baPayload.data(), baPayload.length());

            if(1 != 1) {
                int nCurVecSize = m_bufRecvIAP[nIapModuleIdx].size();

                QByteArray baBadFrames;
                for(int j=0; j<nCurVecSize; j++) {
                    baBadFrames.push_back(m_bufRecvIAP[nIapModuleIdx][j]);
                }
                qDebug() << Q_FUNC_INFO << "--------- Recv Data B" << baBadFrames.size() << baBadFrames.toHex(' ').toUpper() << baBadFrames;
            }
        }
        else if(0 <= nPcrModuleIdx && DEF_PCR_MODULE_SIZE > nPcrModuleIdx) {
            // Write an frame data
            m_bufRecvPCR[nPcrModuleIdx].write(baPayload.data(), baPayload.length());
        }

        // Startup concatenated data
        m_semaphore.release();
    }
}

/*
 * Function:    slotReplyACK
 * Description: reply an ACK command to CAN
 * Paramer:     QByteArray baPacket - the data package
 *              bool bQueued - is process later
 * Return:      int - the length of data size
 */
int CanManage::slotReplyACK(QByteArray baPacket, bool bQueued)
{
    qDebug() << Q_FUNC_INFO << baPacket.size() << bQueued;
    int nRetVal = 0;

    if(DEF_PKG_FIRST_PART < baPacket.size()) {
        QByteArray baACK;
        for(int i=0; i<DEF_PKG_FIRST_PART; i++) {
            baACK.push_back(baPacket.at(i));
        }

        // set the CMD
        baACK[6] = E_PKG_CT_ACK;

        // set the data length
        baACK[DEF_PKG_FIRST_PART-2] = 0x0;
        baACK[DEF_PKG_FIRST_PART-1] = 0x0;

        unsigned char *pData = (unsigned char *)baACK.data();
        quint16 unCRC = getCRC16(pData, baACK.size(), 0);

        QByteArray baHex;
        unsigned char ucHigh = (unCRC >> 8) & 0xff;
        unsigned char ucLow = unCRC & 0xff;
        baACK.push_back(ucHigh);
        baACK.push_back(ucLow);

        // send an ACK frame data to CAN bus
        int nFrameIdACK = 0xff;
        signalSendFrameData(nFrameIdACK, baACK, false);

        nRetVal = baACK.size();
    }

    qDebug() << Q_FUNC_INFO << nRetVal;
    return nRetVal;
}

/*
 * Function:    _concatenatedDataFromIAP
 * Description: Concatenated data packet from CAN
 * Paramer:     int nCanIdx - the index of CAN Number
 * Return:      void
 */
void CanManage::_concatenatedDataFromIAP(int nCanIdx)
{
    QMutexLocker mtxLocker(&m_mtxProc);
    if(0 > nCanIdx || DEF_IAP_MODULE_SIZE <= nCanIdx) return;

    int nCurVecSize = m_bufRecvIAP[nCanIdx].size();
    if(DEF_MIN_PKG_SIZE <= nCurVecSize) {
        if(1 == 1) {
//            while (DEF_MIN_PKG_SIZE <= nCurVecSize) {
//                if((DEF_PKG_HEADER1 == (unsigned char)m_bufRecvIAP[nCanIdx][0])
//                        && (DEF_PKG_HEADER2 == (unsigned char)m_bufRecvIAP[nCanIdx][1])
//                        && (DEF_PKG_HEADER3 == (unsigned char)m_bufRecvIAP[nCanIdx][2])
//                        && (DEF_PKG_HEADER4 == (unsigned char)m_bufRecvIAP[nCanIdx][3])) {
//                    break;
//                }

//                baBadFrames.push_back(m_bufRecvIAP[nCanIdx][0]);
//                m_bufRecvIAP[nCanIdx].pop_front();
//            }

            // Delete excess data before the command header (0x40 0x4d 0x44 0x78)
            QByteArray baBadFrames;
            for(int i=0; i<nCurVecSize - 3; i++) {
                if((DEF_PKG_HEADER1 == (unsigned char)m_bufRecvIAP[nCanIdx][0])
                        && (DEF_PKG_HEADER2 == (unsigned char)m_bufRecvIAP[nCanIdx][1])
                        && (DEF_PKG_HEADER3 == (unsigned char)m_bufRecvIAP[nCanIdx][2])
                        && (DEF_PKG_HEADER4 == (unsigned char)m_bufRecvIAP[nCanIdx][3])) {
                    break;
                }

                baBadFrames.push_back(m_bufRecvIAP[nCanIdx][0]);
                m_bufRecvIAP[nCanIdx].pop_front();
            }

            if(0 < baBadFrames.size()) {
                // Print the invalid data
                qDebug() << Q_FUNC_INFO << "--------- Header Invalid" << nCanIdx << nCurVecSize << baBadFrames.size() << baBadFrames.toHex(' ').toUpper() << baBadFrames;
            }
        }

        // We must to calcute the vector size again
        nCurVecSize = m_bufRecvIAP[nCanIdx].size();
        if(DEF_MIN_PKG_SIZE <= nCurVecSize) {
            // Start process data frames
            if((DEF_PKG_HEADER1 == (unsigned char)m_bufRecvIAP[nCanIdx][0])
                    && (DEF_PKG_HEADER2 == (unsigned char)m_bufRecvIAP[nCanIdx][1])
                    && (DEF_PKG_HEADER3 == (unsigned char)m_bufRecvIAP[nCanIdx][2])
                    && (DEF_PKG_HEADER4 == (unsigned char)m_bufRecvIAP[nCanIdx][3])) {

                unsigned short usCmdID = (unsigned char)m_bufRecvIAP[nCanIdx][6];
                unsigned short usDestinationID = (unsigned char)m_bufRecvIAP[nCanIdx][6];
                unsigned short usRecvDataLen = ((unsigned char)m_bufRecvIAP[nCanIdx][15] << 8) | (unsigned char)m_bufRecvIAP[nCanIdx][14];
                unsigned int unPkgLen = DEF_PKG_MIN_SIZE + usRecvDataLen;

                qDebug() << Q_FUNC_INFO << nCanIdx << nCurVecSize << usCmdID << usDestinationID << unPkgLen << usRecvDataLen;

                if((int)unPkgLen <= nCurVecSize) {
                    // The data length value is satisfied
                    int nOffsetCRC = DEF_PKG_FIRST_PART + usRecvDataLen;

                    QByteArray baRawPacket;
                    memset(m_szDataCRC, 0, nOffsetCRC * sizeof(unsigned char));
                    for(int j=0; j<(int)unPkgLen; j++) {
                        baRawPacket.push_back(m_bufRecvIAP[nCanIdx][j]);
                        m_szDataCRC[j] = m_bufRecvIAP[nCanIdx][j];
                    }

                    unsigned short usOldCRC = ((unsigned char)m_szDataCRC[nOffsetCRC + 0]) << 8 | (unsigned char)(m_szDataCRC[nOffsetCRC + 1]);
                    unsigned short usNewCRC = getCRC16(m_szDataCRC, nOffsetCRC, 0);
                    qDebug() << Q_FUNC_INFO << "CRC-16XMODEM" << nCanIdx << unPkgLen << nOffsetCRC << usRecvDataLen << usOldCRC << usNewCRC;

                    if(usOldCRC == usNewCRC) {
                        QByteArray baGoodPacket;
                        baGoodPacket.resize(unPkgLen);
                        m_bufRecvIAP[nCanIdx].read(baGoodPacket.data(), unPkgLen);

                        qDebug() << Q_FUNC_INFO << nCanIdx << baGoodPacket.size() << baGoodPacket.toHex(' ').toUpper();

                        if(0 < baGoodPacket.size()) {
                            // Select an CAN ID
                            unsigned int unCanId;
                            switch (nCanIdx) {
                            case 0: { unCanId = DEF_MOD_ID_IAP1 + 1; break; }
                            case 1: { unCanId = DEF_MOD_ID_IAP2 + 1; break; }
                            case 2: { unCanId = DEF_MOD_ID_IAP3 + 1; break; }
                            case 3: { unCanId = DEF_MOD_ID_IAP4 + 1; break; }
                            case 4: { unCanId = DEF_MOD_ID_IAP5 + 1; break; }
                            case 5: { unCanId = DEF_MOD_ID_IAP6 + 1; break; }
                            case 6: { unCanId = DEF_MOD_ID_IAP7 + 1; break; }
                            case 7: { unCanId = DEF_MOD_ID_IAP8 + 1; break; }
                            default:
                                unCanId = DEF_MOD_ID_IAP1 + 1;
                            }

                            // send a signal tell "SerialportPlugin" class
                            emit signalDataReceived(unCanId, baGoodPacket);
                        }
                    }
                    else {
                        QByteArray baBadFrames;
                        for(int j=0; j<(int)unPkgLen; j++) {
                            baBadFrames.push_back(m_szDataCRC[j]);
                        }
                        // Print the invalid data
                        qDebug() << Q_FUNC_INFO << "--------- CRC Invalid" << nCanIdx << baBadFrames.size() << baBadFrames.toHex(' ').toUpper() << baBadFrames;

                        // Delete the header & process data again
                        for(int j=0; j<DEF_PKG_SYNC_SIZE; j++) {
                            m_bufRecvIAP[nCanIdx].pop_front();
                        }
                    }
                }
                else {
                    if(512 < (int)unPkgLen) {
                        // The data length value isn't satisfied, we search the header of next data package
                        int nOffset = -1;
                        for(int i=1; i<nCurVecSize - 3; i++) {
                            // Search the next data frame header
                            if((DEF_PKG_HEADER1 == (unsigned char)m_bufRecvIAP[nCanIdx][i+0])
                                    && (DEF_PKG_HEADER2 == (unsigned char)m_bufRecvIAP[nCanIdx][i+1])
                                    && (DEF_PKG_HEADER3 == (unsigned char)m_bufRecvIAP[nCanIdx][i+2])
                                    && (DEF_PKG_HEADER4 == (unsigned char)m_bufRecvIAP[nCanIdx][i+3])) {
                                nOffset = i;
                                break;
                            }
                        }

                        if(1 < nOffset) {
                            // This data is invalid
                            QByteArray baBadFrames;
                            for(int i=0; i<DEF_MIN_PKG_SIZE; i++) {
                                baBadFrames.push_back(m_bufRecvIAP[nCanIdx][i]);
                            }
                            // Print the invalid data
                            qDebug() << Q_FUNC_INFO << "--------- Length Invalid" << nCanIdx << unPkgLen << baBadFrames.size() << baBadFrames.toHex(' ').toUpper();

                            // Delete the header & process data again
                            for(int j=0; j<DEF_PKG_SYNC_SIZE; j++) {
                                m_bufRecvIAP[nCanIdx].pop_front();
                            }
                        }
                    }
                }
            }
        }
    }
}

/*
 * Function:    _concatenatedDataFromPCR
 * Description: Concatenated PCR data packet from CAN
 * Paramer:     int nCanIdx - the index of CAN Number
 * Return:      void
 */
void CanManage::_concatenatedDataFromPCR(int nCanIdx)
{
    QMutexLocker mtxLocker(&m_mtxProc);

    if(0 > nCanIdx || DEF_PCR_MODULE_SIZE <= nCanIdx) return;

do_proc:
    while(0 < m_bufRecvPCR[nCanIdx].size()) {
        qDebug() << Q_FUNC_INFO << nCanIdx << m_bufRecvPCR[nCanIdx].size();

        // If the value (DEF_MIN_CAN_SIZE) is too large, the same serialbus packet will be parsed many times. Please be careful
        if(DEF_STD_CAN_SIZE > m_bufRecvPCR[nCanIdx].size()) break;

        // Delete excess data before the command header (0x01)
        while (0 < m_bufRecvPCR[nCanIdx].size()) {
            if(DEF_PKG_CAN_INDEX1 == (unsigned char)m_bufRecvPCR[nCanIdx][0]) break;

            m_bufRecvPCR[nCanIdx].pop_front();
        }

        // Check the package header (0x23 0xff 0x01)
        if(4 <= m_bufRecvPCR[nCanIdx].size()) {
            if(DEF_PKG_CAN_HEADER1 != (unsigned char)m_bufRecvPCR[nCanIdx][1]
                || DEF_PKG_CAN_HEADER2 != (unsigned char)m_bufRecvPCR[nCanIdx][2]
                || DEF_PKG_CAN_HEADER3 != (unsigned char)m_bufRecvPCR[nCanIdx][3]) {

                // Data header is invalid
                m_bufRecvPCR[nCanIdx].pop_front();
                // Re-process
                goto do_proc;
            }
            else {
                if(3 * DEF_MIN_CAN_SIZE <= m_bufRecvPCR[nCanIdx].size()) {
                    if(DEF_PKG_CAN_TAIL != (unsigned char)m_bufRecvPCR[nCanIdx][20]) {
                        // Data flag is invalid
                        m_bufRecvPCR[nCanIdx].pop_front();
                        // Re-process
                        goto do_proc;
                    }
                    else if(DEF_PKG_CAN_INDEX2 != (unsigned char)m_bufRecvPCR[nCanIdx][8] || DEF_PKG_CAN_INDEX3 != (unsigned char)m_bufRecvPCR[nCanIdx][16]) {
                        // Data flag is invalid
                        m_bufRecvPCR[nCanIdx].pop_front();
                        // Re-process
                        goto do_proc;
                    }
                    else {
                        // for example:
                        // send: 06 47 65 6E 6F 6D 65 00
                        // recv: 01 23 FF 01 01 01 25 01 02 21 00 69 01 01 00 C3 03 00 70 E7 2A 04 00 01
                        int nCheckSum = 0;
                        for(int i=0; i<DEF_STD_CAN_SIZE - 5; i++) {
                            if(0 != i % 8 && 2 <= i) {
                                nCheckSum += (unsigned char)m_bufRecvPCR[nCanIdx][i];
                            }
                        }

                        unsigned short usNewCRC = nCheckSum & 0xff;
                        unsigned short usOldCRC = (unsigned char)m_bufRecvPCR[nCanIdx][19];
                        qDebug() << Q_FUNC_INFO << m_bufRecvPCR[nCanIdx].size() << usOldCRC << usNewCRC;

                        // Check CRC
                        if(usOldCRC != usNewCRC) {
                            // Delete the header & process data again
                            m_bufRecvPCR[nCanIdx].pop_front();
                            // Re-process
                            goto do_proc;
                        }

                        QByteArray baPacket;
                        baPacket.resize(DEF_STD_CAN_SIZE);
                        m_bufRecvPCR[nCanIdx].read(baPacket.data(), DEF_STD_CAN_SIZE);

                        qDebug() << Q_FUNC_INFO << nCanIdx << baPacket.size() << baPacket.toHex(' ').toUpper();

                        if(0 < baPacket.size()) {
                            // Select an CAN ID
                            int unCanId;
                            switch (nCanIdx) {
                            case 0: { unCanId = DEF_MOD_ID_PCR1 + 1; break; }
                            case 1: { unCanId = DEF_MOD_ID_PCR2 + 1; break; }
                            case 2: { unCanId = DEF_MOD_ID_PCR3 + 1; break; }
                            case 3: { unCanId = DEF_MOD_ID_PCR4 + 1; break; }
                            case 4: { unCanId = DEF_MOD_ID_PCR5 + 1; break; }
                            case 5: { unCanId = DEF_MOD_ID_PCR6 + 1; break; }
                            case 6: { unCanId = DEF_MOD_ID_PCR7 + 1; break; }
                            case 7: { unCanId = DEF_MOD_ID_PCR8 + 1; break; }
                            default:
                                unCanId = DEF_MOD_ID_PCR1 + 1;
                            }

                            // send a signal tell "SerialportPlugin" class
                            emit signalDataReceived(unCanId, baPacket);
                        }
                    }
                }
                else if(2 * DEF_MIN_CAN_SIZE <= m_bufRecvPCR[nCanIdx].size()) {
                    if(DEF_PKG_CAN_INDEX2 != (unsigned char)m_bufRecvPCR[nCanIdx][8]) {
                        // Data flag is invalid
                        m_bufRecvPCR[nCanIdx].pop_front();
                        // Re-process
                        goto do_proc;
                    }
                }
            }
        }
    }
}
